import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

/**
 * @author IBRAHIMLI NIhat
 * @author BAYRAMOV Matin
 * 
 */
public class Scheduling {

	private Graph graph;

	private List<Processor> processors;

	public Scheduling(Graph graph, int processors) {
		graph.reGenerateGraphe();
		this.setGraph(graph);
		this.setProcessors(new ArrayList<Processor>());
		this.generateProcessors(processors);
	}

	/**
	 * Generate processors for scheduling
	 * 
	 * @param number
	 */
	public void generateProcessors(int number) {
		int i = 0;
		while (i < number) {
			this.addProcessor(new Processor());
			i++;
		}
	}

	public Scheduling() {
		this(new Graph(), 4);
	}

	public void criticalTaskScheduling() {
		
		// Compute bottom level for each task in graph
		this.graph.computeBottomLevel();
		
		this.graph.computeTopLevel();
		
		System.out.println(this.graph.tasksTopLevel());
		System.out.println("getMakeSpan : "+this.graph.getMakeSpan());

		// List of all scheduled task, empty at the beginning
		List<Task> scheduledTasks = new ArrayList<Task>();

		// List of all unScheduled task, all task at the beginning
		List<Task> unScheduledTasks = new ArrayList<Task>();

		List<Task> alpha = new ArrayList<Task>();

		for (Task task : this.graph.getTasks()) {
		Task newTask = task;
		unScheduledTasks.add(newTask);
		if (task.getType() == 1) {
			alpha.add(newTask);
			}
		}
		

		while (!unScheduledTasks.isEmpty()) {

			Processor proc = this.getEarliestStarTime();

			System.out.print("alpha\n");
			for (int i = 0, size = alpha.size(); i < size; i++) {
				System.out.print(alpha.get(i)+" -> "+alpha.get(i).getPriority()+"\n");
			}
			System.out.println();
			
			Task task = this.graph.getTheHighestPriorityTask(alpha);
			
			
			if ((proc.getAvaibleTime() <= task.getTopLevel())
					|| proc.getId() == this.processors.size()) {

				proc.addTask(task);
				task.setTopLevel(proc.getAvaibleTime());
			} else {

				if (proc.getId() < this.processors.size()) {

					proc = this.processors.get(proc.getId() + 1);
					proc.addTask(task);
				}
			}

			scheduledTasks.add(task);
			task.setScheduled(true);
			

			// Update the priority of task's successors
			this.graph.computeSuccessorsTopLevel(task);

			System.out.print("unScheduled : ");
			for (int i = 0, size = unScheduledTasks.size(); i < size; i++) {
				System.out.print(unScheduledTasks.get(i)+", ");
			}
			
			System.out.println("\n");
			
			for (int i = 0; i < unScheduledTasks.size(); i++) {
				if (unScheduledTasks.get(i).getId() == task.getId()) {
					System.out.println("task scheduled : "+unScheduledTasks.get(i));
					unScheduledTasks.remove(i);
					break;
				}
			}
			
			
			System.out.print("unScheduled : ");
			for (int i = 0, size = unScheduledTasks.size(); i < size; i++) {
				System.out.print(unScheduledTasks.get(i)+", ");
			}
			System.out.println("\n");			
			
			// Put free successors of task in alpha
			
			List<Task> freeSuccessorTasks = this.getFreeSuccessorTask(task);
			if (freeSuccessorTasks.size() == 0) {
				System.out.println("\nfreeSuccessorTasks send nothing");
			} else {
				System.out.print("freeSuccessorTasks : ");
				for (int i = 0, size = freeSuccessorTasks.size(); i < size; i++) {
					System.out.print(freeSuccessorTasks.get(i)+", ");
				}
				System.out.println("\n");
			}
			alpha.addAll(freeSuccessorTasks);
			
			
			
			if (!alpha.remove(task)) {
				System.out.println("Impossible to remove task from alpha");
			}
			
			
			if (alpha.size() == 0) {
				System.out.println("ALPHA IS NULL no processus found : ");
				break;
			}
			
		}
		System.out.println("getMakeSpan : "+this.graph.getMakeSpan());
	}

	public void addProcessor(Processor processor) {
		this.processors.add(processor);
	}

	public Processor getEarliestStarTime() {

		int earliestAvaibleTime = 0;
		int avaibleTime = 0;
		int earliestStartTimeProcessorId = 0;

		for (int i = 0, size = this.processors.size(); i < size; i++) {
			avaibleTime = this.processors.get(i).getAvaibleTime();

			if (avaibleTime < earliestAvaibleTime) {
				earliestAvaibleTime = avaibleTime;
				earliestStartTimeProcessorId = i;
			}
		}

		return processors.get(earliestStartTimeProcessorId);
	}

	/**
	 * 
	 * @param task
	 * @return
	 */
	public List<Task> getFreeSuccessorTask(Task task) {
		int free;

		List<Task> freeSuccessorTasks = new ArrayList<Task>();
		Iterator<Entry<Task, Integer>> iter = task.getSuccessor().entrySet()
				.iterator();

		System.out.println("try to find free successsors of "+task);
		while (iter.hasNext()) {
			

			Map.Entry<Task, Integer> entry = (Map.Entry<Task, Integer>) iter
					.next();
			Task successor = entry.getKey();

			if (successor.isScheduled()) {
				continue;
			}

			Iterator<Entry<Task, Integer>> iterator = successor
					.getPredecessor().entrySet().iterator();

			int allSheduled = 0;
			while (iterator.hasNext()) {
				
				Map.Entry<Task, Integer> entry1 = (Map.Entry<Task, Integer>) iterator
						.next();
				Task predecessor = entry1.getKey();
				
				if (!predecessor.isScheduled()) {
					allSheduled = 1;
				}
			}
			if (allSheduled == 0) {
				freeSuccessorTasks.add(successor);
			}
		}

		return freeSuccessorTasks;
	}
	
	public String processorsToString() {
		String str = "";
		for (int i = 0; i < this.processors.size(); i++) {
			str += this.processors.get(i).toString();
		}
		return str;
	}

	/**
	 * @return the processors
	 */
	public List<Processor> getProcessors() {
		return processors;
	}

	/**
	 * @param processors
	 *            the processors to set
	 */
	public void setProcessors(List<Processor> processors) {
		this.processors = processors;
	}

	/**
	 * @return the graph
	 */
	public Graph getGraph() {
		return graph;
	}

	/**
	 * @param graph
	 *            the graph to set
	 */
	public void setGraph(Graph graph) {
		this.graph = graph;
	}

}
